package com.ruyuan.commodity.common.mamager.impl;

import cn.hutool.json.JSONUtil;
import com.ruyuan.commodity.builder.GoodsDocumentBuilder;
import com.ruyuan.commodity.common.mamager.GoodsInfoIndexManager;
import com.ruyuan.commodity.common.mamager.GoodsWordsManager;
import com.ruyuan.commodity.elasticsearch.*;
import com.ruyuan.commodity.enums.GoodsAuthStatusEnums;
import com.ruyuan.commodity.enums.GoodsDisableStatusEnums;
import com.ruyuan.commodity.enums.GoodsMarketEnableEnums;
import com.ruyuan.commodity.model.goodssearch.enums.GoodsWordsType;
import com.ruyuan.commodity.open.api.client.shop.api.ShopCatOpenApi;
import com.ruyuan.commodity.utils.MapTools;
import com.ruyuan.commodity.utils.StringUtil;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse.AnalyzeToken;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.*;


/**
 * es的商品索引实现
 */
@Service
public class GoodsInfoIndexManagerImpl implements GoodsInfoIndexManager {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 店铺分类的open api
     */
    @Autowired
    protected ShopCatOpenApi shopCatOpenApi;
    /**
     * 商品分词业务管理器
     */
    @Autowired
    protected GoodsWordsManager goodsWordsManager;
    /**
     * ES相关配置
     */
    @Autowired
    protected EsConfig esConfig;
    /**
     * es请求客户端
     */
    @Autowired
    private RestHighLevelClient highLevelClient;
    /**
     * 商品文档构造器
     */
    @Autowired
    private GoodsDocumentBuilder goodsDocumentBuilder;

    /**
     * 将某个商品加入索引
     * @param goodsMap 商品数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addIndex(Map<String, Object> goodsMap) {
        String goodsName = goodsMap.get("goods_name").toString();
        try {
            // 配置文件中定义的索引名字
            String indexName = esConfig.getIndexName() + "_" + EsSettings.GOODS_INDEX_NAME;
            // 1、构造商品的文档信息
            GoodsDocument goodsDocument = goodsDocumentBuilder
                    .buildGoodsBaseInfo(goodsMap)
                    .buildGoodsPriceInfo(goodsMap)
                    .buildGoodsImgInfo(goodsMap)
                    .buildProperty(goodsMap)
                    .buildShopInfo(goodsMap)
                    .buildShoppingInfo(goodsMap)
                    .create();
            // 2、将索引文档转化为 map结构 并构ES造索引文档创建的请求
            Map<String, Object> goodsIndexMap = MapTools.objectToMap(goodsDocument);
            IndexRequest indexRequest = new IndexRequest(indexName, EsSettings.GOODS_TYPE_NAME,
                    goodsDocument.getGoodsId().toString())
                    .source(JSONUtil.toJsonStr(goodsIndexMap), XContentType.JSON)
                    .opType(DocWriteRequest.OpType.CREATE);

            highLevelClient.index(indexRequest, RequestOptions.DEFAULT);

            // 3、对审核通过且没有下架且没有删除的商品使用ik_max_word进行分词处理后入库
            boolean flag = GoodsDisableStatusEnums.NO_DEL.getCode().equals(goodsDocument.getDisabled())
                    && GoodsMarketEnableEnums.MARKET_ENABLE_UP.getCode().equals(goodsDocument.getMarketEnable())
                    && GoodsAuthStatusEnums.AUTH_STATUS_VERIFY_SUCCESS.getCode().equals(goodsDocument.getIsAuth());
            if (flag) {
                List<String> wordsList = toWordsList(goodsName);
                logger.info("wordsList  [{}]", JSONUtil.toJsonStr(wordsList));
                // 分词入库
                this.wordsToDb(wordsList);
            }
        } catch (ElasticsearchStatusException elasticsearchStatusException) {
            if (elasticsearchStatusException.status().equals(RestStatus.CONFLICT)) {
                logger.info("商品[{}]已经在之前创建过了，因此创建该商品文档时发生冲突异常", goodsName);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("为商品[{}]生成索引异常", goodsName, e);
            throw new RuntimeException("为商品[" + goodsName + "]生成索引异常", e);
        }
    }

    /**
     * 更新某个商品的索引
     * @param goods 商品数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateIndex(Map<String, Object> goods) throws IOException {
        // 删除
        this.deleteIndex(goods);
        // 添加
        this.addIndex(goods);
    }

    /**
     * 更新
     * @param goods 商品数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteIndex(Map<String, Object> goods) throws IOException {
        // 配置文件中定义的索引名字
        String indexName = esConfig.getIndexName()+"_"+ EsSettings.GOODS_INDEX_NAME;

        DeleteRequest deleteRequest = new DeleteRequest(indexName,
                EsSettings.GOODS_TYPE_NAME, goods.get("goods_id").toString());
        highLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);

        String goodsName = goods.get("goods_name").toString();
        List<String> wordsList = toWordsList(goodsName);
        this.deleteWords(wordsList);
    }

    /**
     * 将list中的分词删除
     *
     * @param wordsList
     */
    protected void deleteWords(List<String> wordsList) {
        wordsList = removeDuplicate(wordsList);
        for (String words : wordsList) {
            this.goodsWordsManager.delete(words);
        }
    }

    /**
     * 获取分词结果
     *
     * @param txt 文本
     * @return 传入分词后的文本list
     */
    protected List<String> toWordsList(String txt) {
        // 配置文件中定义的索引名字
        String indexName = esConfig.getIndexName()+"_"+ EsSettings.GOODS_INDEX_NAME;

        List<String> tokenList = new ArrayList<String>();
        AnalyzeRequest analyzeRequest = new AnalyzeRequest();
        analyzeRequest.index(indexName) ;
        analyzeRequest.text(txt) ;
        // 分词
        analyzeRequest.analyzer("ik_max_word");
        analyzeRequest.analyzer("ik_max_word");
        IndicesClient indicesClient = highLevelClient.indices();
        try {
            AnalyzeResponse analyze = indicesClient.analyze(analyzeRequest, RequestOptions.DEFAULT);
            List<AnalyzeToken> listAnalysis = analyze.getTokens() ;
            for (AnalyzeToken token : listAnalysis) {
                tokenList.add(token.getTerm());
            }
            return tokenList;
        } catch (IOException e) {
            logger.error("获取分词结果异常");
        }
        return null ;
    }


    /**
     * 将分词结果写入数据库
     * @param wordsList 分词列表
     */
    protected void wordsToDb(List<String> wordsList) {
        wordsList = removeDuplicate(wordsList);
        for (String words : wordsList) {
            goodsWordsManager.addWords(words);
        }
    }
    /**
     * 初始化索引
     * @param list 索引数据
     * @param index 数量
     * @return 是否是生成成功
     */
    @Override
    public boolean addAll(List<Map<String, Object>> list, int index) throws IOException {
        //配置文件中定义的索引名字
        String indexName = esConfig.getIndexName() + "_" + EsSettings.GOODS_INDEX_NAME;

        GetIndexRequest exist = new GetIndexRequest(indexName);
        boolean exists = highLevelClient.indices().exists(exist, RequestOptions.DEFAULT);
        if (exists) {
            // 删除所有的商品文档
            for (Map<String, Object> goods : list) {

                // 删除goods的所有索引
                DeleteRequest deleteRequest = new DeleteRequest(indexName)
                        .id(goods.get("goods_id").toString())
                        .type(EsSettings.GOODS_TYPE_NAME);
                // 删除索引
                highLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
                // 删除分词
                goodsWordsManager.delete(GoodsWordsType.SYSTEM, 0L);
            }
        }

        boolean hasError =false;

        //循环生成索引
        for (Map<String, Object> goods : list) {
            try {
                // 生成优惠价格索引
                goods.put("discount_price", 0L);
                this.addIndex(goods);
            } catch (Exception e) {
                hasError =true;
                logger.error( StringUtil.toString(goods.get("goods_name")) + "索引生成异常" ,e);
            }

        }

        return hasError;
    }

    /**
     * list去重
     * @param list
     * @return
     */
    protected List<String> removeDuplicate(List<String> list){
        List<String> listTemp = new ArrayList<>();
        for (String words:list) {
            if(!listTemp.contains(words)){
                listTemp.add(words);
            }
        }
        return listTemp;
    }


}
